package com.lins.store.bo;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import com.lins.store.constant.UserEnum;
import com.lins.msm.bo.MsmService;
import com.lins.store.dto.ChangePasswordDTO;
import com.lins.store.dto.LoginDTO;
import com.lins.store.dto.RegisterDTO;
import com.lins.store.entity.UserEntity;
import com.lins.store.entity.ex.*;
import com.lins.store.mapper.UserMapper;
import com.lins.common.util.SessionUtils;
import com.lins.common.util.VerifyCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;


@Slf4j
@Service("userService")
@Transactional
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MsmService msmService;

    public List<UserEntity> getUserList() {
        List<UserEntity> list = userMapper.getUserList();
        //重置创建时间格式
        list.forEach(u -> u.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(u.getCreatedTime())));
        return list;
    }

    public Integer delUserByUid(String uid) {
        //物理删除
//        return userMapper.delUserByUid(uid);

        //逻辑删除，改变状态
        return userMapper.changeUserState(Integer.parseInt(uid), UserEnum.USER_DELETED);
    }

    public UserEntity getByUid(String uid) {
        return null;
    }

    public void changePassword(ChangePasswordDTO dto) {
        UserEntity result = userMapper.findByUid(dto.getUid());
        iUserNotFoundException(result);
        String salt = result.getSalt();
        // 将参数oldPassword结合盐值加密，得到oldMd5Password
        String oldMd5Password = getMd5Password(dto.getOldPassword(), salt);
        if (!result.getPassword().contentEquals(oldMd5Password)) {
            throw new PasswordNotMatchException("原密码错误");
        }
        // 将参数newPassword结合盐值加密，得到newMd5Password
        String newMd5Password = getMd5Password(dto.getNewPassword(), salt);
        Date now = new Date();
        Integer rows = userMapper.updatePasswordByUid(dto.getUid(), newMd5Password, dto.getUsername(), now);
        if (rows != 1) {
            throw new UpdateException("修改密码时出现错误，请联系管理员");
        }
    }

    public UserEntity login(LoginDTO dto,HttpSession session) {

        checkVerification(dto.getVerification());

        UserEntity result = userMapper.findByUsername(dto.getUsername());
        //查询用户信息，结果异常判断
        iUserNotFoundException(result);
        // 从查询结果中获取盐值
        String salt = result.getSalt();
        // 调用getMd5Password()方法，将参数password和salt结合起来进行加密
        String md5Password = getMd5Password(dto.getPassword(), salt);
        // 不匹配
        if (!result.getPassword().equals(md5Password)) {
            throw new PasswordNotMatchException("密码错误");
        }

        //检查用户状态
        checkUserState(result);


        /**
         * 2022/11/17重构登录思路：
         * 登录可分账号密码登录，或短信验证登录，或其他第三等接口如微信/QQ登录
         * 若账号密码登录，在账号密码验证通过后，验证用户登录IP，通过可登录，不通过需短信验证(获取验证码之前需人机验证)。
         * 若短信登录，通过后可直接登录。
         **/

        //验证是否为常用ip地址登录
//        String realIp = null;
//        List<String> ipHeadList = Stream.of("X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "X-Real-IP").collect(Collectors.toList());
//        for (String ipHead : ipHeadList) {
//            String ip = request.getHeader(ipHead);
//            if (!(null == ip || 0 == ip.length() || "unknown".equalsIgnoreCase(ip))) {
//                realIp = request.getHeader(ipHead).split(",")[0];
//            }
//        }
//        realIp = "0:0:0:0:0:0:0:1".equals(request.getRemoteAddr()) ? "127.0.0.1" : request.getRemoteAddr();
//        System.out.println(realIp);

        //获取用户ip地址(因前后端不分离，所以从前端过来的请求，全部都是本机。但通过内网穿透后，外网直接访问接口得到的还是本机)
        /*HttpServletRequest servletRequest = HttpContextUtil.getHttpServletRequest();
        String ipAddr = IPUtil.getIpAddr(servletRequest);*/


        /**
         * 拿到真实IP以后，调用接口得到地址，与数据库的常用地址或上次登录地址做对比，对比若不同则需要短信验证。
         * 全部验证通过后，保存用户常用地址和上次登录地址。
         * 若想更加完善，可维护一张用户登录信息表。
         * 常用地址为max(count())
         * 根据登录时间排序，取最近一条数据为上次登录信息。
         */

        //不能全部返回，控制返回一部分数据
        UserEntity userEntity = new UserEntity();
        // 将查询结果中的uid、username、avatar封装到新的user对象中
        userEntity.setUid(result.getUid());
        userEntity.setUsername(result.getUsername());
        userEntity.setAvatar(result.getAvatar());
        userEntity.setLevel(result.getLevel());
        userEntity.setState(result.getState());

        //修改用户的状态为“在线-1”
        userMapper.changeUserState(result.getUid(),1);
        return userEntity;
    }

    //不允许多点同时登录，判断用户当前状态
    public void checkUserState(UserEntity user){
        switch (user.getState()){
            case 1:
                throw new PasswordNotMatchException("该账户已经在别处登录，请注销登录后重新尝试登录");
            case 2:
                throw new PasswordNotMatchException("该账户已经被管理员删除，如需帮助请联系管理员");
        }
    }

    public Integer logout(Integer uid) {
        //注销前，若用户状态为2-被删除，则无须改变
        if (userMapper.findByUid(uid.toString()).getState() == 2) return 0;
        return userMapper.changeUserState(uid, 0);
    }

    public String reg(RegisterDTO reg) {
        //校验 验证码
        checkVerification(reg.getVerification());

        String username = reg.getUsername();
        UserEntity result = userMapper.findByUsername(username);
        if (result != null) throw new UsernameDuplicateException("你尝试注册的用户名“" + username + "”已经被占用");
        result = new UserEntity();
        result.setState(0); //设置状态：0-离线
        result.setCreatedUser(username);
        Date now = new Date();
        result.setCreatedTime(now);
        result.setModifiedUser(username);
        result.setModifiedTime(now);
        String salt = UUID.randomUUID().toString().toUpperCase(); //全大写的String随机数作为盐值
        String md5Password = getMd5Password(result.getPassword(), salt);
        result.setPassword(md5Password);
        result.setSalt(salt);
        Integer rows = userMapper.insert(result);
        if (rows != 1) throw new InsertException("添加用户数据出现未知错误，请联系系统管理员");
        return username;
    }

    public void getImage(HttpSession session, HttpServletResponse response) throws IOException {
        //生成验证码
        String verification = VerifyCodeUtils.generateVerifyCode(4);
        //验证码放入session
        session.setAttribute("verification",verification);
        //设置验证码的session有效期60秒（同一个浏览器在本实例是同一个session，所以不仅是验证码，登录以后session有效期也是60秒）
        session.setMaxInactiveInterval(60);
        //验证码存入图片
        ServletOutputStream os = response.getOutputStream();
        response.setContentType("image/png");
        VerifyCodeUtils.outputImage(110,30,os,verification);
        log.info("图片验证码："+verification);
        //不同的浏览器，因session不同，所以各自存储的验证码，不受影响
    }

    //获取验证码的另一种方式
    public void getYzmImage(HttpServletRequest request, HttpServletResponse response) {
        try {
            //数字类型随机数生成
            RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
            //图片长宽
            LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
            //随机数放入图片
            lineCaptcha.setGenerator(randomGenerator);
            //重新生成code
            lineCaptcha.createCode();
            //图片写出去
            lineCaptcha.write(response.getOutputStream());

            //获取session实例
            SessionUtils sessionUtils = SessionUtils.getInstance();
            //创建session对象
            HttpSession session = request.getSession();
            session.removeAttribute("checkCode");
            //放入验证码
            session.setAttribute("checkCode",lineCaptcha.getCode());
            log.info("图片验证码："+lineCaptcha.getCode());
            //存储60秒
            session.setMaxInactiveInterval(60);
            //放入session实例
            sessionUtils.addSession(session);
            //关闭流
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * md5加密算法  转字节转大写
     *  加密规则：
     *  1、无视原始密码的强度
     *  2、使用UUID作为盐值，在原始密码的左右两侧拼接
     *  3、循环加密3次
     */
    private String getMd5Password(String password, String salt) {
        for (int i = 0; i < 3; i++) password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
        //加密3次后的密码
        return password;
    }

    private void iUserNotFoundException(UserEntity result){
        if (result == null) throw new UserNotFoundException("用户不存在");
    }

    /**
     * 检查验证码
     * @Author: linshuo
     * @Date: 2023/4/28 9:44
     * @Param:   ver
     * @return: boolean
     **/
    private boolean checkVerification(String ver){
        //获取请求
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String verification = (String) request.getSession().getAttribute("verification");
        if (!ver.equalsIgnoreCase(verification)){
            //验证码不一致
            throw new PasswordNotMatchException("验证码"+verification+"错误");
        }
        return true;
    }

    public void sendPhoneCode(RegisterDTO dto){
        //图形验证码
        if (checkVerification(dto.getVerification())){
           msmService.sendReady(dto.getPhone(), "1");
        }else {
            throw new SendMsmException("图形验证码错误");
        }
    }

}
